/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

extern crate alloc;

use crate::commands::common_command::*;
use crate::common::constants::*;
use crate::common::types::*;
use crate::common::{AuthType, ErrorCode};
use crate::entry::user_auth_ffi::CommandId;
use crate::entry::user_auth_ffi::*;
use crate::traits::command_manager::{AccessPolicy, CommandHandler};
use crate::traits::user_db_manager::{CredentialFilter, Credential, UserDbManagerRegistry};
use crate::traits::executor_manager::{ExecutorFilter, ExecutorInfo, ExecutorManagerRegistry};
use crate::{impl_command_handler, log_e, log_i, Box};
use crate::Vec;
use core::convert::TryFrom;

// GetCredential - implementation
#[derive(Clone)]
pub struct GetCredentialInput {
    pub user_id: i32,
    pub auth_type: AuthType,
}

impl TryFrom<GetCredentialInputFfi> for GetCredentialInput {
    type Error = ErrorCode;

    fn try_from(ffi: GetCredentialInputFfi) -> Result<Self, ErrorCode> {
        Ok(GetCredentialInput {
            user_id: ffi.user_id,
            auth_type: AuthType::try_from(ffi.auth_type)?,
        })
    }
}

#[derive(Clone, Default)]
pub struct GetCredentialOutput {
    pub infos: Vec<CredentialInfo>,
}

impl TryFrom<GetCredentialOutput> for GetCredentialOutputFfi {
    type Error = ErrorCode;

    fn try_from(value: GetCredentialOutput) -> core::result::Result<Self, ErrorCode> {
        Ok(GetCredentialOutputFfi {
            infos: value.infos.try_into()?,
        })
    }
}

pub trait GetCredentialCommandExecutor {
    fn execute(&self, input: GetCredentialInput) -> Result<GetCredentialOutput, ErrorCode>;
}

pub struct DefaultGetCredentialCommandExecutor;

impl GetCredentialCommandExecutor for DefaultGetCredentialCommandExecutor {
    fn execute(&self, input: GetCredentialInput) -> Result<GetCredentialOutput, ErrorCode> {

        let credential_list = self.get_credential_list(input.user_id, input.auth_type)?;

        let credential_info_list = self.get_credential_info_list(credential_list)?;

        let get_credential_output = GetCredentialOutput {
            infos: credential_info_list
        };
        
        Ok(get_credential_output)
    }
}

impl DefaultGetCredentialCommandExecutor {
    fn get_credential_list(&self, user_id: i32, auth_type: AuthType) -> Result<Vec<Credential>, ErrorCode> {
        let filter = Box::new(move |cred: &Credential| {
            cred.user_id == user_id && cred.auth_type == auth_type
        });
        
        let credential_list = UserDbManagerRegistry::get().get_credential_list(filter)?;

        Ok(credential_list)
    }

    fn get_credential_info_list(&self, credential_list: Vec<Credential>) -> Result<Vec<CredentialInfo>, ErrorCode> {
        let mut credential_info_list: Vec<CredentialInfo> = Vec::new();

        for credential in credential_list {
            let credential_info = self.get_credential_info(credential)?;

            credential_info_list.push(credential_info);
        }

        Ok(credential_info_list)
    }

    fn get_credential_info(&self, credential: Credential) -> Result<CredentialInfo, ErrorCode> {
        let credential_id = credential.credential_id;

        let auth_type = credential.auth_type;
        let executor_sensor_hint = credential.executor_sensor_hint;
        let filter: ExecutorFilter = Box::new(move |executor: &ExecutorInfo| {
            executor.auth_type == auth_type && executor.sensor_hint == executor_sensor_hint
        });
        let executor_index = ExecutorManagerRegistry::get().get_executor(filter)?.executor_index;

        let template_id = credential.template_id;
        let auth_type = credential.auth_type;
        let executor_matcher = credential.executor_matcher;
        let executor_sensor_hint = credential.executor_sensor_hint;
        let auth_sub_type = credential.credential_type as i32;
        let is_abandoned = false;
        let validity_period = UserDbManagerRegistry::get().get_credential_valid_peroid(credential_id)?;

        let credential_info = CredentialInfo {
            credential_id: credential_id,
            executor_index: executor_index,
            template_id: template_id,
            auth_type: auth_type,
            executor_matcher: executor_matcher,
            executor_sensor_hint: executor_sensor_hint,
            auth_sub_type: auth_sub_type,
            is_abandoned: is_abandoned,
            validity_period: validity_period
        };

        Ok(credential_info)
    }
}

impl_command_handler!(
    GetCredentialCommandHandler,
    GetCredentialInputFfi,
    GetCredentialInput,
    GetCredentialOutputFfi,
    Box<dyn GetCredentialCommandExecutor>
);

// GetCredentialById - implementation
pub type GetCredentialByIdInput = GetCredentialByIdInputFfi;

#[derive(Clone, Default)]
pub struct GetCredentialByIdOutput {
    pub credential_info: CredentialInfo,
}

impl TryFrom<GetCredentialByIdOutput> for GetCredentialByIdOutputFfi {
    type Error = ErrorCode;

    fn try_from(value: GetCredentialByIdOutput) -> core::result::Result<Self, ErrorCode> {
        Ok(GetCredentialByIdOutputFfi {
            info: value.credential_info.try_into()?,
        })
    }
}

pub trait GetCredentialByIdCommandExecutor {
    fn execute(&self, input: GetCredentialByIdInput) -> Result<GetCredentialByIdOutput, ErrorCode>;
}

pub struct DefaultGetCredentialByIdCommandExecutor;

impl GetCredentialByIdCommandExecutor for DefaultGetCredentialByIdCommandExecutor {
    fn execute(
        &self,
        _input: GetCredentialByIdInput,
    ) -> Result<GetCredentialByIdOutput, ErrorCode> {
        let credential_filter: CredentialFilter = Box::new(move |cred: &Credential| {
            cred.credential_id == CredentialId(_input.credential_id)
        });
        let credential = UserDbManagerRegistry::get().get_credential(credential_filter)?;

        let executor_filter: ExecutorFilter = Box::new(move |executor: &ExecutorInfo| {
            executor.auth_type == credential.auth_type &&
            executor.sensor_hint == credential.executor_sensor_hint &&
            executor.role == ExecutorRole::AllInOne
        });
        let executor_info = ExecutorManagerRegistry::get().get_executor(executor_filter)?;

        let validity_period = UserDbManagerRegistry::get().get_credential_valid_peroid(credential.credential_id)?;
        let credential_info: CredentialInfo = CredentialInfo {
            credential_id: credential.credential_id,
            executor_index: executor_info.executor_index,
            template_id: credential.template_id,
            auth_type: credential.auth_type,
            executor_matcher: credential.executor_matcher,
            executor_sensor_hint: credential.executor_sensor_hint,
            auth_sub_type: credential.credential_type as i32,
            is_abandoned: false,
            validity_period: validity_period,
        };

        Ok(GetCredentialByIdOutput{ credential_info })
    }
}

impl_command_handler!(
    GetCredentialByIdCommandHandler,
    GetCredentialByIdInputFfi,
    GetCredentialByIdInput,
    GetCredentialByIdOutputFfi,
    Box<dyn GetCredentialByIdCommandExecutor>
);

// DeleteCredential - implementation
pub type DeleteCredentialInput = DeleteCredentialInputFfi;

#[derive(Clone, Default)]
pub struct DeleteCredentialOutput {
    pub operate_type: i32,
    pub schedule_info: ScheduleInfo,
    pub credential_infos: Vec<CredentialInfo>,
}

impl TryFrom<DeleteCredentialOutput> for DeleteCredentialOutputFfi {
    type Error = ErrorCode;

    fn try_from(value: DeleteCredentialOutput) -> core::result::Result<Self, ErrorCode> {
        Ok(DeleteCredentialOutputFfi {
            operate_type: value.operate_type,
            schedule_info: value.schedule_info.try_into()?,
            credential_infos: value.credential_infos.try_into()?,
        })
    }
}

pub trait DeleteCredentialCommandExecutor {
    fn execute(&self, input: DeleteCredentialInput) -> Result<DeleteCredentialOutput, ErrorCode>;
}

pub struct DefaultDeleteCredentialCommandExecutor;

impl DeleteCredentialCommandExecutor for DefaultDeleteCredentialCommandExecutor {
    fn execute(&self, _input: DeleteCredentialInput) -> Result<DeleteCredentialOutput, ErrorCode> {
        Err(ErrorCode::GeneralError)
    }
}

impl_command_handler!(
    DeleteCredentialCommandHandler,
    DeleteCredentialInputFfi,
    DeleteCredentialInput,
    DeleteCredentialOutputFfi,
    Box<dyn DeleteCredentialCommandExecutor>
);

// ClearUnavailableCredential - implementation
pub type ClearUnavailableCredentialInput = ClearUnavailableCredentialInputFfi;

#[derive(Clone, Default)]
pub struct ClearUnavailableCredentialOutput {
    pub cleared_credential_infos: Vec<CredentialInfo>,
}

impl TryFrom<ClearUnavailableCredentialOutput> for ClearUnavailableCredentialOutputFfi {
    type Error = ErrorCode;

    fn try_from(value: ClearUnavailableCredentialOutput) -> core::result::Result<Self, ErrorCode> {
        Ok(ClearUnavailableCredentialOutputFfi {
            credentials: value.cleared_credential_infos.try_into()?,
        })
    }
}

pub trait ClearUnavailableCredentialCommandExecutor {
    fn execute(
        &self,
        input: ClearUnavailableCredentialInput,
    ) -> Result<ClearUnavailableCredentialOutput, ErrorCode>;
}

pub struct DefaultClearUnavailableCredentialCommandExecutor;

impl ClearUnavailableCredentialCommandExecutor
    for DefaultClearUnavailableCredentialCommandExecutor
{
    fn execute(
        &self,
        _input: ClearUnavailableCredentialInput,
    ) -> Result<ClearUnavailableCredentialOutput, ErrorCode> {
        Ok(ClearUnavailableCredentialOutput {
            cleared_credential_infos: Vec::new(),
        })
    }
}

impl_command_handler!(
    ClearUnavailableCredentialCommandHandler,
    ClearUnavailableCredentialInputFfi,
    ClearUnavailableCredentialInput,
    ClearUnavailableCredentialOutputFfi,
    Box<dyn ClearUnavailableCredentialCommandExecutor>
);
